package com.huiquan.management.service;

import java.io.File;
import java.io.IOException;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Date;
import java.util.HashMap;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;
import org.springframework.web.servlet.ModelAndView;

import com.huiquan.foundation.constant.ModuleID;
import com.huiquan.foundation.util.BusinessUtil;
import com.huiquan.framework.base.BaseService;
import com.huiquan.framework.base.ReturnCode;
import com.huiquan.framework.base.ReturnData;
import com.huiquan.framework.constant.BaseContants;
import com.huiquan.framework.utils.FileUtils;
import com.huiquan.framework.utils.ReturnUtil;
import com.huiquan.management.constant.ManagementContants;
import com.huiquan.management.constant.MigrateCode;
import com.huiquan.management.dao.MigrateDao;
import com.huiquan.management.domain.Migrate;
import com.huiquan.management.domain.MigrateDto;
import com.huiquan.management.util.MigrateSwitch;

@Service
public class MigrateService extends BaseService {

	@Autowired
	private MigrateDao migrateDao;
	@Autowired
	private MigrateSwitch migrateSwitch;

	public ModelAndView list(String keyword) {

		Map<String, Object> param = new HashMap<>();
		if (keyword != null && !keyword.isEmpty()) {
			param.put("keyword", "%" + keyword + "%");
		}

		List<Migrate> migrateList = migrateDao.retrieveList(param);

		List<MigrateDto> list = new ArrayList<>();
		List<MigrateDto> delList = new ArrayList<>();
		for (Migrate migrate : migrateList) {
			MigrateDto md = new MigrateDto();
			md.setId(migrate.getId());
			md.setModule(migrate.getModule());
			md.setDescription(migrate.getDescription());
			md.setTypeName(ManagementContants.migrateTypeName.get(migrate.getType()));
			md.setLocation(migrate.getLocation());
			md.setUsername(migrate.getUsername());
			md.setCode(migrate.getCode());

			if (BaseContants.FLAG_NO.equals(migrate.getDelFlag())) {
				list.add(md);
			} else {
				delList.add(md);
			}
		}

		Map<String, String> showSearch = new LinkedHashMap<>();
		showSearch.put("keyword", "模块");

		Map<String, Object> map = new HashMap<>();
		map.put("list", list);
		map.put("delList", delList);
		map.put("showSearch", showSearch);

		return new ModelAndView("management/index", map);
	}

	public ReturnData init() {

		LOGGER.info("迁移code初始化开始！");

		List<Migrate> migrateList = migrateDao.retrieveList();

		Map<String, Migrate> migrateCodeMap = new HashMap<>();
		List<String> migrateCodeList = new ArrayList<>();
		for (Migrate migrate : migrateList) {
			migrateCodeMap.put(migrate.getCode(), migrate);
			migrateCodeList.add(migrate.getCode());
		}

		// 遍历所有的迁移code
		for (MigrateCode code : MigrateCode.values()) {
			ModuleID mid = code.getModuleID();
			if (mid == null) {
				LOGGER.error("迁移code对应的模块ID不存在：" + code.getCode());
				return ReturnUtil.fail(ReturnCode.EXCEPTION);
			}

			Migrate migrate = migrateCodeMap.get(code.getCode());
			if (migrate != null) {
				// 如果数据库中已存在则校验是否需要修改模块名称和内容描述
				if (!checkCodeAndData(code, mid, migrate)) {
					migrate.setModule(mid.getDesc());
					migrate.setDescription(code.getDesc());
					migrate.setType(code.getType());
					migrate.setLocation(code.getLocation());
					migrate.setSep(code.getSep());

					migrateDao.update(migrate);
				}

				migrateCodeList.remove(code.getCode());
			} else {
				// 数据库中不存在则添加入数据库
				migrate = new Migrate();
				migrate.setCode(code.getCode());
				migrate.setModule(mid.getDesc());
				migrate.setDescription(code.getDesc());
				migrate.setType(code.getType());
				migrate.setLocation(code.getLocation());
				migrate.setSep(code.getSep());

				migrateDao.insert(migrate);
			}
		}

		if (!migrateCodeList.isEmpty()) {
			for (String code : migrateCodeList) {
				LOGGER.error("迁移code对应的枚举类型不存在：" + code);
			}
			return ReturnUtil.fail(ReturnCode.EXCEPTION);
		}

		return ReturnUtil.success();
	}

	private boolean checkCodeAndData(MigrateCode code, ModuleID mid, Migrate migrate) {

		boolean moduleFlag = mid.getDesc().equals(migrate.getModule());
		boolean descriptionFlag = code.getDesc().equals(migrate.getDescription());
		boolean typeFlag = code.getType() == migrate.getType();
		boolean locationFlag = code.getLocation().equals(migrate.getLocation());
		boolean sepFlag = code.getSep().equals(migrate.getSep());

		if (moduleFlag && descriptionFlag && typeFlag && locationFlag && sepFlag) {
			return true;
		}
		return false;
	}

	public ReturnData update(long id, String username, String type, String location) {

		Migrate migrate = new Migrate();
		migrate.setId(id);
		migrate.setUsername(username);

		migrateDao.update(migrate);

		return ReturnUtil.success();
	}

	@Value("${migrate.bak.file.path.pre}")
	private String bakFilePathPre;

	/**
	 * 根据迁移编码获取本地备份文件地址，并将该地址初始化文件或文件夹
	 * 
	 * @param mc
	 *            迁移编码
	 * @param isSingleFile
	 *            是否单一文件，是则返回文件路径，否则返回文件夹路径
	 * @return
	 */
	public String getBakFilePathByCode(MigrateCode mc, boolean isSingleFile) throws Exception {

		// 根据编码获取备份文件夹名称
		String[] codeNameSplit = mc.name().split("_", 2);
		String moduleName = codeNameSplit[0];

		String bakFilePath = bakFilePathPre + File.separator + moduleName.toLowerCase();
		if (codeNameSplit.length > 1) {
			bakFilePath += File.separator + codeNameSplit[1].toLowerCase();
		}

		// 初始化备份文件夹
		File bakFileDict = new File(bakFilePath);
		if (bakFileDict.exists()) {
			if (bakFileDict.isFile()) {
				// 如果这个路径是个文件则报错
				LOGGER.error("数据迁移的备份文件夹路径是一个文件！");
				throw new RuntimeException("系统错误：数据迁移的备份文件夹路径是一个文件");
			}
		} else {
			// 如果该文件夹不存在，则创建
			bakFileDict.mkdirs();
		}

		// 获取当前日期
		String todayStr = new SimpleDateFormat("yyyyMMdd").format(new Date());

		// 获取当前不存在的一个备份路径
		bakFilePath += File.separator + todayStr;
		File bakFile = new File(bakFilePath);
		int i = 1;
		while (bakFile.exists()) {
			bakFile = new File(bakFilePath + "_" + i++);
		}

		// 根据是否单一文件来返回文件或文件夹
		if (isSingleFile) {
			bakFile.createNewFile();
		} else {
			bakFile.mkdirs();
		}

		return bakFile.getAbsolutePath();
	}

	/**
	 * 根据code获取migrate对象，做一定的校验
	 * 
	 * @param mc
	 * @return
	 */
	private Migrate getMigrateByCode(MigrateCode mc, int type) {
		// 根据code获取迁移表数据
		Migrate migrate = migrateDao.retrieveObjectByCode(mc.getCode());

		if (migrate == null || migrate.getLocation().isEmpty()) {

			LOGGER.error("需要迁移的code未初始化！");
			throw new RuntimeException("系统错误：需要迁移的code未初始化");

		} else if (type != migrate.getType()) {

			LOGGER.error("code对应的迁移类型错误：" + mc.getDesc());
			throw new RuntimeException("系统错误：code对应的迁移类型错误：" + mc.getDesc());

		}
		return migrate;
	}

	@Value("${migrate.single.file.script}")
	private String singleFileScript;
	@Value("${migrate.batch.file.script}")
	private String batchFileScript;

	/**
	 * 通过code获取导出到181的路径，将备份文件导出到181
	 * 
	 * @param mc
	 * @param bakFilePath
	 * @param isSingleFile
	 * @throws IOException
	 */
	public void migrateFileByCodeAndBak(MigrateCode mc, String bakFilePath, boolean isSingleFile) throws IOException {

		// 根据code获取迁移表数据
		Migrate migrate = getMigrateByCode(mc, ManagementContants.MIGRATE_TYPE_FILE);

		// 根据是否单个文件来调用不同的脚本
		String script = singleFileScript;
		if (!isSingleFile) {
			script = batchFileScript;
		}
		BusinessUtil.excuteShell(new String[] { script, bakFilePath, migrate.getLocation() });
	}

	/**
	 * 将字符串内容备份到code对应的地址，然后导出到对应181地址
	 * 
	 * @param mc
	 * @param content
	 * @throws Exception
	 */
	public void migrateFileByCodeAndString(MigrateCode mc, String content) throws Exception {

		// 备份文件
		String bakPath = getBakFilePathByCode(mc, true);
		FileUtils.generateFile(bakPath, content);

		// 导出到181
		migrateFileByCodeAndBak(mc, bakPath, true);
	}

	@Value("${migrate.hive.script}")
	private String hiveScript;

	/**
	 * 通过code获取导出到181的路径，将备份文件导出到HIVE
	 * 
	 * @param mc
	 * @param bakFilePath
	 * @throws IOException
	 */
	public void migrateHiveByCodeAndBak(MigrateCode mc, String bakFilePath) throws IOException {

		// 根据code获取迁移表数据
		Migrate migrate = getMigrateByCode(mc, ManagementContants.MIGRATE_TYPE_HIVE);

		migrateHiveByCodeAndBak(migrate, bakFilePath);
	}

	/**
	 * 将备份文件导出到HIVE
	 * 
	 * @param mc
	 * @param bakFilePath
	 * @throws IOException
	 */
	public void migrateHiveByCodeAndBak(Migrate migrate, String bakFilePath) throws IOException {

		// 调用脚本迁移
		BusinessUtil.excuteShell(new String[] { hiveScript, bakFilePath, migrate.getLocation() });
	}

	/**
	 * 将字符串内容备份到code对应的地址，然后导出到对应HIVE表
	 * 
	 * @param mc
	 * @param content
	 * @throws Exception
	 */
	public void migrateHiveByCodeAndString(MigrateCode mc, String content) throws Exception {

		// 备份文件
		String bakPath = getBakFilePathByCode(mc, true);
		FileUtils.generateFile(bakPath, content);

		// 导出到181
		migrateHiveByCodeAndBak(mc, bakPath);
	}

	/**
	 * 根据code获取字符串内容，备份到code对应的地址，然后导出到对应HIVE表
	 * 
	 * @param mc
	 * @param content
	 * @throws Exception
	 */
	public void migrateHiveByCode(MigrateCode mc) throws Exception {

		// 根据code获取迁移表数据
		Migrate migrate = getMigrateByCode(mc, ManagementContants.MIGRATE_TYPE_HIVE);

		// 备份文件
		String bakPath = getBakFilePathByCode(mc, true);
		FileUtils.generateFile(bakPath, getContentByCode(mc, migrate));

		// 导出到181
		migrateHiveByCodeAndBak(migrate, bakPath);
	}

	private String getContentByCode(MigrateCode mc, Migrate migrate) {

		List<Map<String, String>> datas = migrateDao.retrieveMigrateData(mc);

		StringBuffer result = new StringBuffer();
		if (datas != null && !datas.isEmpty()) {
			if (MigrateCode.ZS_NEW_TRAIN.equals(mc)) {

			} else if (MigrateCode.ZS_NEW_TRAIN_BME.equals(mc)) {

			} else {
				// 获取参数个数
				Set<String> keySet = datas.get(0).keySet();

				int paramCount = 0;
				while (keySet.contains("r" + paramCount + 1)) {
					paramCount++;
				}

				if (paramCount != keySet.size()) {
					LOGGER.error("数据迁移的结果集参数个数有误：" + mc.name());
					throw new RuntimeException("数据迁移的结果集参数个数有误：" + mc.name());
				}

				// 根据约定好的参数排序、分隔符将结果集拼凑为字符串
				String sep = migrate.getSep();
				for (Map<String, String> data : datas) {
					for (int i = 1; i <= paramCount; i++) {
						result.append(data.get("r" + i));
						result.append(ManagementContants.migrateSepValue.get(sep));
					}
					result.append("\n");
				}
			}
		}

		return result.toString();
	}

	public ReturnData excute(String code) throws Exception {

		for (MigrateCode mc : MigrateCode.values()) {
			if (mc.getCode().equals(code)) {
				migrateSwitch.migrateByCode(mc);
				break;
			}
		}

		return ReturnUtil.success();
	}

	/**
	 * 根据code获取migrate对象，做一定的校验
	 * 
	 * @param mc
	 * @return
	 */
	public Migrate getMigrateByCode(MigrateCode mc) {
		// 根据code获取迁移表数据
		Migrate migrate = migrateDao.retrieveObjectByCode(mc.getCode());

		if (migrate == null || migrate.getLocation().isEmpty()) {

			LOGGER.error("需要迁移的code未初始化！");
			throw new RuntimeException("系统错误：需要迁移的code未初始化");

		}

		return migrate;
	}
}
